﻿ 
using Quartz;
using Quartz.Collection;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using SX.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Ly.Scheduler.NewService
{
    public static class JobManager
    {
        public const string PUBLICJOB_KEY = "PublicJob";
        public static void InitJobScheduler(IScheduler scheduler)
        {
            var service = new BackgroundJobService();
            var jobs = service.GetAllJobs();
            var groups = scheduler.GetJobGroupNames();
            foreach (var group in groups)
            {
                var jobsInSche = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(group));
                foreach (var job in jobsInSche)
                {
                    bool checkHaveJob = false;
                    // 如果任务的名字是【PublicJob】则跳过
                    if (job.Name == PUBLICJOB_KEY)
                    {
                        continue;
                    }
                    foreach (var j in jobs)
                    {
                        if (j.Id.Value.ToString() == job.Name)
                        {
                            checkHaveJob = true;
                            break;
                        }
                    }
                    if (checkHaveJob)
                    {
                        continue;
                    }
                    Console.WriteLine("删除job: {0}:{1}", group, job.Name);
                    scheduler.DeleteJob(job);
                }
            }
            CreatePublicJob(scheduler);
            if (jobs != null && jobs.Count > 0)
            {
                foreach (var job in jobs)
                {
                    JobKey jobKey = CreateKey(job.Id.Value);
                    if (scheduler.CheckExists(jobKey))
                    {
                        if (job.Status == 0)
                        {
                            scheduler.DeleteJob(jobKey);
                            service.UpdateState(job.Id.Value, 0);
                        }
                        if (job.Status == 4)
                        {
                            scheduler.DeleteJob(jobKey);
                            service.UpdateState(job.Id.Value, 5);
                        }
                        if (job.Status == 5)
                        {
                            scheduler.DeleteJob(jobKey);
                            service.UpdateState(job.Id.Value, 5);
                        }
                        else
                        {
                            //scheduler.DeleteJob(jobKey); // 防止需要更新（先删除）
                            ReplaceScheduleJob(scheduler, job); // 再重新添加
                            if (scheduler.CheckExists(jobKey))
                            {
                                service.UpdateState(job.Id.Value, 1);
                            }
                            else
                            {
                                service.UpdateState(job.Id.Value, 0);
                            }
                        }
                    }
                    else
                    {
                        if (job.Status == 1 || job.Status == 2 || job.Status == 3)
                        {
                            ScheduleJob(scheduler, job);
                            if (scheduler.CheckExists(jobKey))
                            {
                                service.UpdateState(job.Id.Value, 1);
                            }
                            else
                            {
                                service.UpdateState(job.Id.Value, 0);
                            }
                        }
                        else if (job.Status == 4) // 暂停
                        {
                            service.UpdateState(job.Id.Value, 5);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 创建后台管理job (10秒运行一次)
        /// </summary>
        /// <param name="scheduler"></param>
        private static void CreatePublicJob(IScheduler scheduler)
        {
            var PublicJobKey = CreateKey(PUBLICJOB_KEY);
            //var publicJob = scheduler.GetJobDetail(PublicJobKey);
            if (scheduler.CheckExists(PublicJobKey))
            {
                scheduler.DeleteJob(PublicJobKey);
            }
            IJobDetail jobPublic = JobBuilder.Create<PublicJob>().WithIdentity(PublicJobKey).StoreDurably(true).RequestRecovery().Build();
            jobPublic.JobDataMap.Add("JobName", "隐藏后台管理作业，触发其他作业用");
            ISimpleTrigger triggerPublic = (ISimpleTrigger)TriggerBuilder.Create()
              .WithIdentity(PUBLICJOB_KEY+"trigger", PUBLICJOB_KEY+ "triggergroup")
              .StartNow()   
              .WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever()) 
              .Build();
            scheduler.ScheduleJob(jobPublic, triggerPublic);
        }

        public static void ScheduleJob(IScheduler scheduler, BackgroundJob jobInfo)
        {

            if (jobInfo.trigger != null)
            {
                jobInfo.trigger = jobInfo.trigger.Where(t => t.Status == 1).ToList();
            }
            if (jobInfo.trigger == null || jobInfo.trigger.Count == 0)
            {

                new BackgroundJobService().AddJobExcuteLog(jobInfo.Id.Value, jobInfo.Name, null, DateTime.Now, null, null, "没有触发器，无法启动该任务", 1, "没有触发器，无法启动该任务");
                return;
            }
            foreach (var triger in jobInfo.trigger)
            {
                if (triger.Status == 1 && !triger.CronExpression.IsCronExpression())
                {
                    new BackgroundJobService().AddJobExcuteLog(jobInfo.Id.Value, jobInfo.Name, null, DateTime.Now, null, null, triger.CronExpression + "不是正确的Cron表达式,无法启动该任务", 1, triger.CronExpression + "不是正确的Cron表达式,无法启动该任务");
                    return;
                }
            }

            var job = CreateJob(jobInfo);
            if (job == null)
            {
                return;
            }
            for (int i = 0; i < jobInfo.trigger.Count; i++)
            {
                var t = jobInfo.trigger[i];
                if (t.Status != 1)
                {
                    continue;
                }

                var trigger = TriggerBuilder.Create()
                    .WithIdentity(t.Id.ToString(), t.Id.ToString() + "TriggerGroup")
                    .StartNow()
                    .ForJob(job)
                    .WithCronSchedule(t.CronExpression)
                    .WithDescription(t.Description)
                    .Build();
                if (i == 0)
                {
                    scheduler.ScheduleJob(job, trigger);
                }
                else
                {
                    scheduler.ScheduleJob(trigger);
                } 
            }
        }
        public static IJobDetail CreateJob(BackgroundJob jobInfo)
        {

            Type type = null;
            try
            {
                type = type = ReflectionHelper.GetType(jobInfo.JobType);
            }
            catch (Exception ex)
            {

                type = null;
            }
            if (type == null)
            {
                new BackgroundJobService().AddJobExcuteLog(jobInfo.Id.Value, jobInfo.Name, null, DateTime.Now, null, null, jobInfo.JobType + "无效，无法启动该任务", 1, jobInfo.JobType + "无效，无法启动该任务");
                return null;
            }

            IJobDetail job = JobBuilder.Create(type).WithIdentity(CreateKey(jobInfo.Id.Value)).StoreDurably(true).RequestRecovery().Build();
 
            job.JobDataMap.Add("Parameters", jobInfo.Parameters);
            job.JobDataMap.Add("JobName", jobInfo.Name);

            return job;
        }
        public static void ReplaceScheduleJob(IScheduler scheduler, BackgroundJob jobInfo)
        {
            JobKey jobKey = CreateKey(jobInfo.Id.Value);
            if (jobInfo.trigger != null)
            {
                jobInfo.trigger = jobInfo.trigger.Where(t => t.Status == 1).ToList();
            }
            if (jobInfo.trigger == null || jobInfo.trigger.Count == 0)
            {
                new BackgroundJobService().AddJobExcuteLog(jobInfo.Id.Value, jobInfo.Name, null, DateTime.Now, null, null, "没有触发器，无法启动该任务", 1, "没有触发器，无法启动该任务");
                scheduler.DeleteJob(jobKey);
                return;
            }
            foreach (var triger in jobInfo.trigger)
            {
                if (triger.Status == 1 && !triger.CronExpression.IsCronExpression())
                {
                    new BackgroundJobService().AddJobExcuteLog(jobInfo.Id.Value, jobInfo.Name, null, DateTime.Now, null, null, triger.CronExpression + "不是正确的Cron表达式,无法启动该任务", 1, triger.CronExpression + "不是正确的Cron表达式,无法启动该任务");
                    scheduler.DeleteJob(jobKey);
                    return;
                }
            }

            IJobDetail job = CreateJob(jobInfo);
            if (job == null)
            {
                scheduler.DeleteJob(jobKey);
                return;
            }
            TreeSet<ITrigger> triggerList = new TreeSet<ITrigger>();
            for (int i = 0; i < jobInfo.trigger.Count; i++)
            {
                var t = jobInfo.trigger[i];
                if (t.Status != 1)
                {
                    continue;
                }
                var trigger = TriggerBuilder.Create()
                    .WithIdentity(t.Id.ToString(), t.Id.ToString() + "TriggerGroup")
                    .StartNow()
                    .ForJob(job)
                    .WithCronSchedule(t.CronExpression)
                    .WithDescription(t.Description)
                    .Build();
                triggerList.Add(trigger);
            }
            scheduler.ScheduleJob(job, triggerList, true);
        }

        public static JobKey CreateKey(Guid id)
        {
            JobKey key = new JobKey(id.ToString(), id.ToString() + "Group");
            return key;
        }
        public static JobKey CreateKey(string id)
        {
            JobKey key = new JobKey(id, id + "Group");
            return key;
        }
    }
}
